package com.basic.common.area;

import com.alibaba.fastjson.JSONObject;
import com.basic.common.constant.MagicValueDsConstant;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 描述:GeoHash:TODO
 * DATE:2016/1/5
 * TIME:16:50
 * VERSION:1.0
 * @author 
 */
public class GeoHashDsUtil {

    private LocationDsBean location;
    /**
     *12500km;2630km;378km;430km
     *52.4km;6610m;776m;819m
     */
    /**
     *经纬度转化为geohash长度
     */
    private int hashLength=6;
    /**
     *纬度转化为二进制长度
     */
    private int latLength=15;
    /**
     *经度转化为二进制长度
     */
    private int lngLength=15;
    /**
     *每格纬度的单位大小
     */
    private double minLat;
    /**
     *每个经度的大小
     */
    private double minLng;
    private static final char[] CHARS={'0','1','2','3','4','5','6','7',
            '8','9','b','c','d','e','f','g','h','j','k','m','n',
            'p','q','r','s','t','u','v','w','x','y','z'};

    public GeoHashDsUtil(double lat, double lng){
        location=new LocationDsBean(lat,lng);
        setMinLatLng();
    }

    public int gethashLength(){
        return hashLength;
    }

    /**
     *@Author:lulei
     *@Description:设置经纬度的最小单位
     */
    private void setMinLatLng(){
        minLat= LocationDsBean.MAXLAT- LocationDsBean.MINLAT;
        for(int i=0;i<latLength;i++){
            minLat/=2.0;
        }
        minLng= LocationDsBean.MAXLNG- LocationDsBean.MINLNG;
        for(int i=0;i<lngLength;i++){
            minLng/=2.0;
        }
    }

    /**
     *@return
     *@Author:lulei
     *@Description:求所在坐标点及周围点组成的九个
     */
    public List<String> getGeoHashBase32For9(){
        double leftLat=location.getLat()-minLat;
        double rightLat=location.getLat()+minLat;
        double upLng=location.getLng()-minLng;
        double downLng=location.getLng()+minLng;
        List<String> base32For9=new ArrayList<String>();
        //左侧从上到下3个
        String leftUp=getGeoHashBase32(leftLat,upLng);
        if(!(leftUp==null||"".equals(leftUp))){
            base32For9.add(leftUp);
        }
        String leftMid=getGeoHashBase32(leftLat,location.getLng());
        if(!(leftMid==null||"".equals(leftMid))){
            base32For9.add(leftMid);
        }
        String leftDown=getGeoHashBase32(leftLat,downLng);
        if(!(leftDown==null||"".equals(leftDown))){
            base32For9.add(leftDown);
        }
        //中间从上到下3个
        String midUp=getGeoHashBase32(location.getLat(),upLng);
        if(!(midUp==null||"".equals(midUp))){
            base32For9.add(midUp);
        }
        String midMid=getGeoHashBase32(location.getLat(),location.getLng());
        if(!(midMid==null||"".equals(midMid))){
            base32For9.add(midMid);
        }
        String  midDown=getGeoHashBase32(location.getLat(),downLng);
        if(!(midDown==null||"".equals(midDown))){
            base32For9.add(midDown);
        }
        //右侧从上到下3个
        String  rightUp=getGeoHashBase32(rightLat,upLng);
        if(!(rightUp==null||"".equals(rightUp))){
            base32For9.add(rightUp);
        }
        String rightMid=getGeoHashBase32(rightLat,location.getLng());
        if(!(rightMid==null||"".equals(rightMid))){
            base32For9.add(rightMid);
        }
        String rightDown=getGeoHashBase32(rightLat,downLng);
        if(!(rightDown==null||"".equals(rightDown))){
            base32For9.add(rightDown);
        }
        return base32For9;
    }

    /**
     *@paramlength
     *@return
     *@Author:lulei
     *@Description:设置经纬度转化为geohash长度
     */
    public boolean sethashLength(int length){
        if(length<1){
            return false;
        }
        hashLength=length;
        latLength=(length*5)/2;
        if(length% MagicValueDsConstant.INTEGER_2.intValue() ==0){
            lngLength=latLength;
        }else{
            lngLength=latLength+1;
        }
        setMinLatLng();
        return true;
    }

    /**
     *@return
     *@Author:lulei
     *@Description:获取经纬度的base32字符串
     */
    public String getGeoHashBase32(){
        return getGeoHashBase32(location.getLat(),location.getLng());
    }

    /**
     *@paramlat
     *@paramlng
     *@return
     *@Author:lulei
     *@Description:获取经纬度的base32字符串
     */
    private String getGeoHashBase32(double lat,double lng){

        boolean[] bools=getGeoBinary(lat,lng);
        if(bools==null){
            return null;
        }
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<bools.length;i=i+ MagicValueDsConstant.INTEGER_5.intValue()){
            boolean[]base32=new boolean[5];
            for(int j = 0; j< MagicValueDsConstant.INTEGER_5.intValue(); j++){
                base32[j]=bools[i+j];
            }
            char cha=getBase32Char(base32);
            if('\0'==cha){
                return null;
            }
            sb.append(cha);
        }
        return sb.toString();
    }


    private char getBase32Char(boolean[] base32){
        if(base32==null||base32.length!= MagicValueDsConstant.INTEGER_5.intValue()){
            return '\0';
        }
        int num=0;
        for(boolean bool:base32){
            num<<=1;
            if(bool){
                num+=1;
            }
        }
        return CHARS[num%CHARS.length];
    }

    /**
     *@paramlat
     *@paramlng
     *@return
     *@Author:lulei
     *@Description:获取坐标的geo二进制字符串
     */
    private boolean[]getGeoBinary(double lat,double lng){
        boolean[] latArray=getHashArray(lat, LocationDsBean.MINLAT, LocationDsBean.MAXLAT,latLength);
        boolean[] lngArray=getHashArray(lng, LocationDsBean.MINLNG, LocationDsBean.MAXLNG,lngLength);
        return merge(latArray,lngArray);
    }

    /**
     *@paramlatArray
     *@paramlngArray
     *@return
     *@Author:lulei
     *@Description:合并经纬度二进制
     */
    private boolean[] merge(boolean[] latArray,boolean[]lngArray){
        if(latArray==null||lngArray==null){
            return null;
        }
        boolean[]result=new boolean[lngArray.length+latArray.length];
        Arrays.fill(result, false);
        for(int i=0;i<lngArray.length;i++){
            result[2*i]=lngArray[i];
        }
        for(int i=0;i<latArray.length;i++){
            result[2*i+1]=latArray[i];
        }
        return result;
    }

    /**
     *@paramvalue
     *@parammin
     *@parammax
     *@return
     *@Author:lulei
     *@Description:将数字转化为geohash二进制字符串
     */
    private boolean[] getHashArray(double value,double min,double max,int length){
        if(value<min||value>max){
            return null;
        }
        if(length<1){
            return null;
        }
        boolean[]result=new boolean[length];
        for(int i=0;i<length;i++){
            double mid=(min+max)/2.0;
            if(value>mid){
                result[i]=true;
                min=mid;
            }else{
                result[i]=false;
                max=mid;
            }
        }
        return result;
    }


    public static void main(String[]args){
       GeoHashDsUtil g=new GeoHashDsUtil(40.381068,116.696422);
       System.out.println(g.getGeoHashBase32());
       System.out.println(JSONObject.toJSON(g.getGeoHashBase32For9()));
    }

}
